var ext = function(){
  var debug = 0;
  var extid = chrome.runtime.id;
  var domains;
  this.popup = function(){
    //
    /*
    待完成：
    提交问题url至
    https://www.laxiao.com/api/copy/url?url=https://www.jb51.net/article/81770.htm
    */
    //
    $(document).ready(function(){
      domains           = getStorage('domains') || [];
      //当前域名
      chrome.tabs.getSelected(function(tab) {
        var tabid       = tab.id;
        var domain      = getDomain(tab.url);
        var icon        = tab.favIconUrl ? tab.favIconUrl : 'img/tab.png';
        if(!domain && (typeof tab.pendingUrl) != 'undefined'){
          domain          = getDomain(tab.pendingUrl);
        }
        if(domain){
          $('#popup .domain .icon').html('<img src="'+icon+'">');
          $('#popup .domain .url').html(domain);
          if(domains.indexOf(domain) > -1){
            $('#popup .copy').removeClass('off').addClass('on');
            $('#popup .copy .tips').html(l('tips'));
          }else{
            $('#popup .copy').removeClass('on').addClass('off');
            $('#popup .copy .tips').html(l('tips'));
          }
          $('#popup .copy').on('mouseover mouseout','',function(event){
            var turn        = $('#popup .copy').hasClass('on') ? 'off' : 'on';
            if(event.type == "mouseover"){
              //鼠标悬浮
              if(turn == 'on'){
                $('#popup .copy .tips').html(l('enableTips'));
              }else{
                $('#popup .copy .tips').html(l('disableTips'));
              }
            }else if(event.type == "mouseout"){
              //鼠标离开
              $('#popup .copy .tips').html(l('tips'));
            }
          });
          $('#popup .copy').on('click','',function(){
            var turn        = $('#popup .copy').hasClass('on') ? 'off' : 'on';
            chrome.runtime.sendMessage({type: "copy",turn: turn}, function(res) {
              if(turn == 'on'){
                $('#popup .copy').removeClass('off').addClass('on');
              }else{
                $('#popup .copy').removeClass('on').addClass('off');
              }
              $('#popup .copy .tips').html(l('tips'));
            });
          });
        }else{
          $('#popup .copy').removeClass('on').addClass('off');
          $('#popup .copy .tips').html(l('cannotTips'));
        }
      });
      //禁止右键
      document.oncontextmenu = function(){
        if(!debug){return false;}
      };
    });
    //刷新当前tab
    var refreshTab = function(){
      chrome.tabs.getSelected(function(tab) {
        if(tab.id > -1){
          chrome.tabs.reload(tab.id);
        }
      });
    };
  };
  this.background = function(){
    $(document).ready(function(){
      domains           = getStorage('domains') || [];
      //监听通讯请求
      chrome.runtime.onMessage.addListener(function(message, sender, reply) {
        var status      = 1;
        if(message.type == 'copy'){
          chrome.tabs.getSelected(function(tab) {
            var tabid     = tab.id;
            var domain    = getDomain(tab.url);
            if(message.turn == 'on'){
              turn('on',tabid,domain);
            }else{
              turn('off',tabid,domain);
            }
          });
        }else if(message.type == 'reload'){
          window.location.reload();
        }
        reply({status:status});
      });

      //切换
      chrome.tabs.onSelectionChanged.addListener(function(tabId, selectInfo) {
        try {
          chrome.tabs.get(tabId, function(tab) {
            var domain        = getDomain(tab.url);
            var icon          = 'img/ico19.png';
            if(!inDomains(domain)){
              icon          = 'img/ico19_disable.png';
            }
            chrome.browserAction.setIcon({
              path: icon,
              tabId: tabId
            });
          });
        } catch (error) {
          log(error);
        }
      });

      //更新
      chrome.tabs.onUpdated.addListener(function(tabid, info, tab) {
        var domain        = getDomain(info.url || tab.url);
        if(inDomains(domain)){
          if (info.status == 'complete') {
            turn('on',tabid,domain);
          }
        }else{
          setIcon('off',tabid);
        }
      });
    });

    //打开页面
    var opentab = function(url,unique,selected){
      var current = 0;
      if(url == ''){return false;}
      if(unique !== false){unique = true;}
      if(selected !== false){selected = true;}
      //判断是否声明 tabs 权限
      var permissions = chrome.app.getDetails()['permissions'];
      var permis  = permissions.indexOf('tabs') > -1 ? true : false;
      if(permis && unique === true){
        chrome.tabs.getAllInWindow(null,function(tabs){
          for (var i=0;i<tabs.length;i++){
            if (tabs[i].url == url){
              chrome.tabs.update(tabs[i].id, {selected:true});return true;
            }
          }
          chrome.tabs.create({url:url,selected:true});return true;
        });
      }else{
        chrome.tabs.create({url:url,selected:selected});return true;
      }
    };
    var turn  = function(type,tabid,domain){
      if(!domain){return false;}
      domains           = getStorage('domains') || [];
      var index         = domains.indexOf(domain);
      if(type == 'on'){
        //增加域名
        if(index == -1){
          domains.push(domain);
        }
        //执行程序
        var src     = chrome.runtime.getURL('js/code.js');
        var code    = "var script = document.createElement('script');script.src = '" + src + "';document.body.appendChild(script);"
        var script  = {code: code,allFrames: true};
        try {
          setIcon(type,tabid);
          chrome.tabs.executeScript(tabid, script);
        } catch (error) {
          log(error);
        }

      }else{
        //设置icon
        setIcon(type,tabid);
        //删除域名
        if(index > -1){
          domains.splice(index, 1);
        }
        //刷新页面
        chrome.tabs.reload(tabid);
      }
      setStorage('domains',domains);
    };
    var inDomains = function(domain){
      domains           = getStorage('domains') || [];
      var index         = domains.indexOf(domain);
      return index === -1 ? false : true;
    };
    var setIcon = function(type,tabid){
      if(type == 'on'){
        chrome.browserAction.setIcon({
          path: 'img/ico19.png',
          tabId: tabid
        });
      }else{
        chrome.browserAction.setIcon({
          path: 'img/ico19_disable.png',
          tabId: tabid
        });
      }
    };

  };
  //获取域名
  var getDomain = function(url) {
    if(!url){return false;}
    var scheme = url.toLowerCase().split("://")[0];
    if (scheme != "http" && scheme != "https") {
      return false;
    }
    var e = url.toLowerCase().split("://")[1].split("/")[0].split(":")[0];
    var k = /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/;
    if (k.test(e)) {
      if (RegExp.$1 < 256 && RegExp.$2 < 256 && RegExp.$3 < 256 && RegExp.$4 < 256) {
        if (RegExp.$1 == 192 && RegExp.$2 == 168) {
          return false
        }
        if (RegExp.$1 == 10) {
          return false
        }
        return e
      } else {
        return false
      }
    } else {
      if (e.substr(0, 4) == "www.") {
        e = e.substr(4)
      }
      var g = Array("com.cn","net.cn", "edu.cn","org.cn","gov.cn","com.co","co.jp","co.kr", "com.hk", "com.br", "co.jp", "co.uk", "com.sg", "edu.au");
      var j = Array("com","net","ru","org","de","cn","br","uk","jp","pl","in","info","it","fr","au","nl","ir","es","ua","gr","cz","ca","ro","eu","biz","tw","za","tv","se","us","mx","edu","co","hu","tr","ar","me","vn","dk","ch","be","at","cc","no","id","kr","sk","pt","fi","my","il","cl","hk","nz","kz","ie","lt","by","sg","su","th","bg","ws","gov","az","asia","pk","rs","tk","hr","lv","ph","pe","mobi","ve","si","ee","sa","pro","fm","ae","to","nu","am","name","ma","lk","ng","xxx","io","ge","ec","uz","mn","eg","bd","cat","mk","uy","ba","do","bz","lu","ke","tn","is","im","pw","la","md","travel","ly","cr","dz","so","al","cy","kg","st","py","gt","kw","cm","vc","pa","li","qa","coop","ps","bo","ag","jobs","sv","om","ug","ms","np","mo","aero","sx","int","tz","jo","lb","gs","af","sh","mu","cu","kh","tj","mm","tc","cx","ac","gg","mil","hn","ni","mt","sc","sd","iq","ci","tl","gh","cd","as","mz","sn","na","re","pr","bh","tt","rw","mg","sy","pn","vg","bw","gd","zw","ao","zm","vu","pf","bn","bt","gl","dj","tel","nf","bf","mr","ad","tm","bi","nr","fo","gi","ai","ye","tf","lc","ht","sm","nc","mv","va","fj","bm","gy","sl","jm","mw","hm","wf","ne","gm","museum","bb","et","je","cv","ky","mc","bs","pg","gp","pm","ki","dm","ls","ml","sz","ga","vi","aw","kp","bj","mp","an","ck","mq","ax","cf","post","sr","lr","yt","km","td","cg","eh","aq","sb","kn","tp","tg","xyz","top","aaa","aarp","abarth","abb","abbott","abbvie","abc","able","abogado","abudhabi","ac","academy","accenture","accountant","accountants","aco","active","actor","ad","adac","ads","adult","ae","aeg","aero","aetna","af","afamilycompany","afl","africa","agakhan","agency","aig","aigo","airbus","airforce","airtel","akdn","al","alfaromeo","alibaba","alipay","allfinanz","allstate","ally","alsace","alstom","am","americanexpress","americanfamily","amex","amfam","amica","amsterdam","an","analytics","android","anquan","anz","aol","apartments","app","apple","aq","aquarelle","ar","arab","aramco","archi","army","arpa","art","arte","as","asda","asia","associates","at","athleta","attorney","au","auction","audi","audible","audio","auspost","author","auto","autos","avianca","aws","axa","azure","ba","baby","baidu","banamex","bananarepublic","band","bank","bar","barcelona","barclaycard","barclays","barefoot","bargains","baseball","basketball","bauhaus","bayern","bb","bbc","bbt","bbva","bcg","bcn","bd","be","beats","beauty","beer","bentley","berlin","best","bestbuy","bet","bharti","bible","bid","bike","bing","bingo","bio","bj","bl","black","blackfriday","blanco","blockbuster","blog","bloomberg","blue","bm","bms","bmw","bnl","bnpparibas","bo","boats","boehringer","bofa","bom","bond","boo","book","booking","boots","bosch","bostik","boston","bot","boutique","box","bq","br","bradesco","bridgestone","broadway","broker","brother","brussels","bs","bt","budapest","bugatti","build","builders","business","buy","buzz","bv","bw","by","bz","bzh","ca","cab","cafe","cal","call","calvinklein","cam","camera","camp","cancerresearch","canon","capetown","capital","capitalone","car","caravan","cards","care","career","careers","cars","cartier","casa","case","caseih","cash","casino","cat","catering","catholic","cba","cbn","cbre","cbs","ceb","center","ceo","cern","cfa","cfd","chanel","channel","chase","chat","cheap","chintai","chloe","christmas","chrome","chrysler","church","cipriani","circle","cisco","citadel","citi","citic","city","cityeats","ck","cl","claims","cleaning","click","clinic","clinique","clothing","cloud","club","clubmed","cm","coach","codes","coffee","college","cologne","comcast","commbank","community","company","compare","computer","comsec","condos","construction","consulting","contact","contractors","cooking","cookingchannel","cool","coop","corsica","country","coupon","coupons","courses","credit","creditcard","creditunion","cricket","crown","crs","cruise","cruises","csc","cu","cuisinella","cv","cw","cymru","cyou","cz","dabur","dad","dance","data","date","dating","datsun","day","dclk","dds","de","deal","dealer","deals","degree","delivery","dell","deloitte","delta","democrat","dental","dentist","desi","design","dev","dhl","diamonds","diet","digital","direct","directory","discount","discover","dish","diy","dnp","docs","doctor","dodge","dog","doha","domains","doosan","dot","download","drive","dtv","dubai","duck","dunlop","duns","dupont","durban","dvag","dvr","dz","earth","eat","ec","eco","edeka","edu","education","ee","eg","eh","email","emerck","energy","engineer","engineering","enterprises","epost","epson","equipment","er","ericsson","erni","es","esq","estate","esurance","et","etisalat","eu","eurovision","eus","events","everbank","exchange","expert","exposed","express","extraspace","fage","fail","fairwinds","faith","family","fan","fans","farm","farmers","fashion","fast","fedex","feedback","ferrari","ferrero","fi","fiat","fidelity","fido","film","final","finance","financial","fire","firestone","firmdale","fish","fishing","fit","fitness","fj","fk","flickr","flights","flir","florist","flowers","flsmidth","fly","fm","fo","foo","food","foodnetwork","football","ford","forex","forsale","forum","foundation","fox","fr","free","fresenius","frl","frogans","frontdoor","frontier","ftr","fujitsu","fujixerox","fun","fund","furniture","futbol","fyi","gal","gallery","gallo","gallup","game","games","gap","garden","gb","gbiz","gd","gdn","ge","gea","gent","genting","george","gf","ggee","gh","gi","gift","gifts","gives","giving","gl","glade","glass","gle","global","globo","gm","gmail","gmbh","gmo","gmx","gn","godaddy","gold","goldpoint","golf","goo","goodhands","goodyear","goog","google","gop","got","gov","gp","gq","gr","grainger","graphics","gratis","green","gripe","grocery","group","gs","gt","gu","guardian","gucci","guge","guide","guitars","guru","gw","gy","hair","hamburg","hangout","haus","hbo","hdfc","hdfcbank","health","healthcare","help","helsinki","here","hermes","hgtv","hiphop","hisamitsu","hitachi","hiv","hkt","hm","hockey","holdings","holiday","homedepot","homegoods","homes","homesense","honda","honeywell","horse","hospital","host","hosting","hot","hoteles","hotels","hotmail","house","how","hr","hsbc","ht","htc","hu","hughes","hyatt","hyundai","ibm","icbc","ice","icu","id","ie","ieee","ifm","iinet","ikano","il","im","imamat","imdb","immo","immobilien","in","industries","infiniti","ing","ink","institute","insurance","insure","int","intel","international","intuit","investments","io","ipiranga","iq","ir","irish","is","iselect","ismaili","ist","istanbul","it","itau","itv","iveco","iwc","jaguar","java","jcb","jcp","je","jeep","jetzt","jewelry","jio","jlc","jll","jm","jmp","jnj","jo","jobs","joburg","jot","joy","jp","jpmorgan","jprs","juegos","juniper","kaufen","kddi","ke","kerryhotels","kerrylogistics","kerryproperties","kfh","kg","kh","ki","kia","kim","kinder","kindle","kitchen","kiwi","km","kn","koeln","komatsu","kosher","kp","kpmg","kpn","kr","krd","kred","kuokgroup","kw","ky","kyoto","kz","la","lacaixa","ladbrokes","lamborghini","lamer","lancaster","lancia","lancome","land","landrover","lanxess","lasalle","lat","latino","latrobe","law","lawyer","lb","lc","lds","lease","leclerc","lefrak","legal","lego","lexus","lgbt","li","liaison","lidl","life","lifeinsurance","lifestyle","lighting","like","lilly","limited","limo","lincoln","linde","link","lipsy","live","living","lixil","lk","loan","loans","locker","locus","loft","lol","london","lotte","lotto","love","lpl","lplfinancial","lr","ls","lt","ltd","ltda","lu","lundbeck","lupin","luxe","luxury","lv","ly","ma","macys","madrid","maif","maison","makeup","man","management","mango","map","market","marketing","markets","marriott","marshalls","maserati","mattel","mba","mc","mcd","mcdonalds","mckinsey","md","med","media","meet","melbourne","meme","memorial","men","menu","meo","merckmsd","metlife","mf","mg","mh","miami","microsoft","mil","mini","mint","mit","mitsubishi","mk","ml","mlb","mls","mm","mma","mn","mo","mobi","mobile","mobily","moda","moe","moi","mom","monash","money","monster","montblanc","mopar","mormon","mortgage","moscow","moto","motorcycles","mov","movie","movistar","mp","mq","mr","ms","msd","mt","mtn","mtpc","mtr","mu","museum","mutual","mutuelle","mv","mw","mx","my","mz","na","nab","nadex","nagoya","name","nationwide","natura","navy","nba","nc","ne","nec","netbank","netflix","network","neustar","new","newholland","news","next","nextdirect","nexus","nf","nfl","ng","ngo","nhk","ni","nico","nike","nikon","ninja","nissan","nissay","nl","no","nokia","northwesternmutual","norton","now","nowruz","nowtv","np","nr","nra","nrw","ntt","nu","nyc","nz","obi","observer","off","office","okinawa","olayan","olayangroup","oldnavy","ollo","om","omega","one","ong","onl","online","onyourside","ooo","open","oracle","orange","organic","orientexpress","origins","osaka","otsuka","ott","ovh","pa","page","pamperedchef","panasonic","panerai","paris","pars","partners","parts","party","passagens","pay","pccw","pe","pet","pf","pfizer","pg","ph","pharmacy","phd","philips","phone","photo","photography","photos","physio","piaget","pics","pictet","pictures","pid","pin","ping","pink","pioneer","pizza","pk","pl","place","play","playstation","plumbing","plus","pm","pn","pnc","pohl","poker","politie","porn","post","pr","pramerica","praxi","press","prime","pro","prod","productions","prof","progressive","promo","properties","property","protection","pru","prudential","ps","pt","pub","pwc","py","qa","qpon","quebec","quest","qvc","racing","radio","raid","re","read","realestate","realtor","realty","recipes","red","redstone","redumbrella","rehab","reise","reisen","reit","reliance","ren","rent","rentals","repair","report","republican","rest","restaurant","review","reviews","rexroth","rich","richardli","ricoh","rightathome","ril","rio","rip","rmit","ro","rocher","rocks","rodeo","rogers","room","rs","rsvp","ru","rugby","ruhr","run","rw","rwe","ryukyu","sa","saarland","safe","safety","sakura","sale","salon","samsclub","samsung","sandvik","sandvikcoromant","sanofi","sap","sapo","sarl","sas","save","saxo","sb","sbi","sbs","sc","sca","scb","schaeffler","schmidt","scholarships","school","schule","schwarz","science","scjohnson","scor","scot","sd","se","search","seat","secure","security","seek","select","sener","services","ses","seven","sew","sex","sexy","sfr","shangrila","sharp","shaw","shell","shia","shiksha","shoes","shop","shopping","shouji","show","showtime","shriram","si","silk","sina","singles","site","sj","sk","ski","skin","sky","skype","sl","sling","sm","smart","smile","sncf","soccer","social","softbank","software","sohu","solar","solutions","song","sony","soy","space","spiegel","spot","spreadbetting","srl","srt","ss","st","stada","staples","star","starhub","statebank","statefarm","statoil","stc","stcgroup","stockholm","storage","store","stream","studio","study","style","su","sucks","supplies","supply","support","surf","surgery","suzuki","sv","swatch","swiftcover","swiss","sx","sy","sydney","symantec","systems","sz","tab","taipei","talk","taobao","target","tatamotors","tatar","tattoo","tax","taxi","tc","tci","td","tdk","team","tech","technology","tel","telecity","telefonica","temasek","tennis","teva","tf","tg","th","thd","theater","theatre","tiaa","tickets","tienda","tiffany","tips","tires","tirol","tj","tjmaxx","tjx","tkmaxx","tl","tm","tmall","tn","to","today","tokyo","tools","toray","toshiba","total","tours","town","toyota","toys","trade","trading","training","travel","travelchannel","travelers","travelersinsurance","trust","trv","tt","tube","tui","tunes","tushu","tv","tvs","tw","tz","ua","ubank","ubs","uconnect","ug","uk","um","unicom","university","uno","uol","ups","us","uy","uz","va","vacations","vana","vanguard","vegas","ventures","verisign","versicherung","vet","vg","vi","viajes","video","vig","viking","villas","vin","vip","virgin","visa","vision","vista","vistaprint","viva","vivo","vlaanderen","vn","vodka","volkswagen","volvo","vote","voting","voto","voyage","vu","vuelos","wales","walmart","walter","wang","wanggou","warman","watch","watches","weather","weatherchannel","webcam","weber","website","wed","wedding","weibo","weir","whoswho","wien","wiki","williamhill","win","windows","wine","winners","wme","wolterskluwer","woodside","work","works","world","wow","wtc","wtf","xbox","xerox","xfinity","xihuan","xin");
      var f = e.split(".");
      var c = f.length;
      var j = f[c - 1];
      var h = f[c - 2] + "." + f[c - 1];
      if (g.indexOf(h) > -1) {
        e = f[c - 3] + "." + h
      } else {
        if (j.indexOf(j) > -1) {
          e = f[c - 2] + "." + j
        }
      }
      return e
    }
  };
  //提取URL参数
  //url : http://www.abc.com/test?key=value 返回 value
  var getUrlParam = function(key,url) {
    var reg = new RegExp("(/?|^|&)" + key + "=([^&]*)(&|$)"); //构造一个含有目标参数的正则表达式对象
    var r = url.substr(1).match(reg); //匹配目标参数
    if (r != null){
      return decodeURIComponent(r[2]);
    }
    return null;
  };

  //获取本地缓存getStorage
  var getStorage = function(key){
    result	= null;
    if (typeof(window.localStorage['e__'+key]) !== "undefined"){
      result = JSON.parse(de(window.localStorage['e__'+key]));
    }
    return result;
  };
  //保存本地缓存setStorage
  var setStorage = function(key,val){
    return window.localStorage['e__'+key] = en(JSON.stringify(val));
  };
  //清空本地缓存clearStorage
  var clearStorage = function(key){
    window.localStorage.removeItem('e__'+key);
  };
  //调试log记录
  var log = function(text){
    debug && console.log(text);
  };
  //语言
  var l = function(id){
    return chrome.i18n.getMessage(id);
  };
  var en = function(input){
    var output = "";
    if(!input){return output;}
    var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
    var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
    var i = 0;
    input = utf16to8(input);
    while (i < input.length) {
      chr1 = input.charCodeAt(i++);
      chr2 = input.charCodeAt(i++);
      chr3 = input.charCodeAt(i++);
      enc1 = chr1 >> 2;
      enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
      enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
      enc4 = chr3 & 63;
      if (isNaN(chr2)) {
        enc3 = enc4 = 64;
      }else if (isNaN(chr3)) {
        enc4 = 64;
      }
      output = output + b64.charAt(enc1) + b64.charAt(enc2) + b64.charAt(enc3) + b64.charAt(enc4);
    }
    return output;
  };
  var de = function(input){
    var output = "";
    if(!input){return output;}
    var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
    var chr1, chr2, chr3;
    var enc1, enc2, enc3, enc4;
    var i = 0;
    input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
    while (i < input.length) {
      enc1 = b64.indexOf(input.charAt(i++));
      enc2 = b64.indexOf(input.charAt(i++));
      enc3 = b64.indexOf(input.charAt(i++));
      enc4 = b64.indexOf(input.charAt(i++));
      chr1 = (enc1 << 2) | (enc2 >> 4);
      chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
      chr3 = ((enc3 & 3) << 6) | enc4;
      output = output + String.fromCharCode(chr1);
      if (enc3 != 64) {
        output = output + String.fromCharCode(chr2);
      }
      if (enc4 != 64) {
        output = output + String.fromCharCode(chr3);
      }
    }
    output = utf8to16(output);
    return output;
  };
  var utf16to8 = function(str){
    var out, i, len, c;
    out = "";
    len = str.length;
    for(i = 0; i < len; i++) {
      c = str.charCodeAt(i);
      if ((c >= 0x0001) && (c <= 0x007F)) {
        out += str.charAt(i);
      } else if (c > 0x07FF) {
        out += String.fromCharCode(0xE0 | ((c >> 12) & 0x0F));
        out += String.fromCharCode(0x80 | ((c >> 6) & 0x3F));
        out += String.fromCharCode(0x80 | ((c >> 0) & 0x3F));
      } else {
        out += String.fromCharCode(0xC0 | ((c >> 6) & 0x1F));
        out += String.fromCharCode(0x80 | ((c >> 0) & 0x3F));
      }
    }
    return out;
  };
  var utf8to16 = function(str) {
    var out, i, len, c;
    var char2, char3;
    out = "";
    len = str.length;
    i = 0;
    while(i < len) {
      c = str.charCodeAt(i++);
      switch(c >> 4) {
        case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
        // 0xxxxxxx
        out += str.charAt(i-1);
        break;
        case 12: case 13:
        // 110x xxxx 10xx xxxx
        char2 = str.charCodeAt(i++);
        out += String.fromCharCode(((c & 0x1F) << 6) | (char2 & 0x3F));
        break;
        case 14:
        // 1110 xxxx 10xx xxxx 10xx xxxx
        char2 = str.charCodeAt(i++);
        char3 = str.charCodeAt(i++);
        out += String.fromCharCode(((c & 0x0F) << 12) |
        ((char2 & 0x3F) << 6) |
        ((char3 & 0x3F) << 0));
        break;
      }
    }
    return out;
  };
};